package org.jeecg.modules.online.low.aspect;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.aspect.annotation.AutoLowApp;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.online.cgform.entity.OnlCgformHead;
import org.jeecg.modules.online.cgform.enums.CgformConstant;
import org.jeecg.modules.online.cgform.model.OnlCgformModel;
import org.jeecg.modules.online.cgform.service.IOnlCgformHeadService;
import org.jeecg.modules.online.cgreport.entity.OnlCgreportHead;
import org.jeecg.modules.online.cgreport.model.OnlCgreportModel;
import org.jeecg.modules.online.graphreport.vo.OnlGraphreportHeadPage;
import org.jeecg.modules.online.low.constant.LowAppConst;
import org.jeecg.modules.online.low.constant.LowAppMenuType;
import org.jeecg.modules.online.low.entity.LowAppMenu;
import org.jeecg.modules.online.low.service.ILowAppMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * lowApp应用切面开发
 *
 * @author sunjianlei
 */
@Aspect
@Slf4j
@Component("lowAppAspect")
public class LowAppAspect {

    @Autowired
    ILowAppMenuService appMenuService;
    @Autowired
    IOnlCgformHeadService cgformHeadService;

    @Pointcut("@annotation(org.jeecg.common.aspect.annotation.AutoLowApp)")
    public void excudeLowAppAspect() {
    }

    /**
     * 自动根据不同的切面类型，执行不同的操作
     *
     * @param pjp
     * @param autoLowApp
     * @return
     * @throws Throwable
     */
    @Around("excudeLowAppAspect() && @annotation(autoLowApp)")
    public Object excudeLowAppAspect(ProceedingJoinPoint pjp, AutoLowApp autoLowApp) throws Throwable {
        switch (autoLowApp.action()) {
            case ADD:
                return this.autoAddAspect(pjp, autoLowApp);
            case DELETE:
                return this.autoDeleteAspect(pjp, autoLowApp);
            case CGFORM_DB_IMPORT:
                // Online表单专用：数据库表转Online表单
                return this.doExcudeCgformSaveDbTable2Online(pjp);
            default:
                return pjp.proceed();
        }
    }

    /**
     * 自动根据业务类型，走不同的新增逻辑
     * Q/hdrTigVhn2pSGe4h016YLAYGNvwZkHHabjkkA48Sh5mbH49xzVfTfEW3ALHDRXupi+Y/Gg/swwGEyL0PuVFg==
     *
     * @param pjp
     * @param autoLowApp
     * @return
     * @throws Throwable
     */
    private Object autoAddAspect(ProceedingJoinPoint pjp, AutoLowApp autoLowApp) throws Throwable {
        switch (autoLowApp.bizType()) {
            case LowAppMenuType.CGFORM:
                return this.doExcudeCgformAdd(pjp);
            case LowAppMenuType.DESFORM:
                return this.doExcudeDesignAdd(pjp);
            case LowAppMenuType.CGREPORT:
                return this.doExcudeCgreportAdd(pjp);
            case LowAppMenuType.GRAPHREPORT:
                return this.doExcudeGraphreportAdd(pjp);
            default:
                return pjp.proceed();
        }
    }

    /**
     * 自动根据注解类型删除应用菜单
     *
     * @param pjp
     * @param autoLowApp
     * @return
     * @throws Throwable
     */
    private Object autoDeleteAspect(ProceedingJoinPoint pjp, AutoLowApp autoLowApp) throws Throwable {
        Object ret = null;
        try {
            ret = this.autoDeleteMenu(pjp, autoLowApp.bizType());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ret == null) {
            return pjp.proceed();
        }
        return ret;
    }

    /**
     * 表单设计器【add】方法切面
     */
    private Object doExcudeDesignAdd(ProceedingJoinPoint pjp) throws Throwable {
        try {
            Object[] args = pjp.getArgs();
            HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
            // 判断Header中是否包含 lowAppId
            String lowAppId = request.getHeader(LowAppConst.X_LOW_APP_ID);
            if (StringUtil.isNotBlank(lowAppId)) {
                Object designFormPage = args[0];
                // 反射类：org.jeecg.modules.online.desform.vo.DesignFormPage
                Class<?> clazz = designFormPage.getClass();
                // 向表单里添加appId属性，关联应用
                clazz.getMethod("setLowAppId", String.class).invoke(designFormPage, lowAppId);
                // 自动生成app菜单
                String desformName = clazz.getMethod("getDesformName").invoke(designFormPage).toString();
                String desformCode = clazz.getMethod("getDesformCode").invoke(designFormPage).toString();
                this.saveLowAppMenu(lowAppId, desformName, desformCode, LowAppMenuType.DESFORM);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pjp.proceed();
    }

    /**
     * Online报表【add】方法切面
     */
    private Object doExcudeCgreportAdd(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = null;
        try {
            HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
            // 判断Header中是否包含 lowAppId
            String lowAppId = request.getHeader(LowAppConst.X_LOW_APP_ID);
            if (StringUtil.isNotBlank(lowAppId)) {
                Object[] args = pjp.getArgs();
                OnlCgreportModel values = (OnlCgreportModel) args[0];
                OnlCgreportHead cgreport = values.getHead();
                // 设置lowAppId
                cgreport.setLowAppId(lowAppId);
                // 直接让方法执行到最后，添加成功后才能获取到添加的ID
                ret = pjp.proceed();
                this.saveLowAppMenu(lowAppId, cgreport.getName(), cgreport.getId(), LowAppMenuType.CGREPORT);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ret == null) {
            return pjp.proceed();
        }
        return ret;
    }

    /**
     * Online图表【add】方法切面
     */
    private Object doExcudeGraphreportAdd(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = null;
        try {
            HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
            // 判断Header中是否包含 lowAppId
            String lowAppId = request.getHeader(LowAppConst.X_LOW_APP_ID);
            if (StringUtil.isNotBlank(lowAppId)) {
                Object[] args = pjp.getArgs();
                OnlGraphreportHeadPage page = (OnlGraphreportHeadPage) args[0];
                // 设置lowAppId
                page.setLowAppId(lowAppId);
                // 直接让方法执行到最后，添加成功后才能获取到添加的ID
                ret = pjp.proceed();
                this.saveLowAppMenu(lowAppId, page.getName(), page.getId(), LowAppMenuType.GRAPHREPORT);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ret == null) {
            return pjp.proceed();
        }
        return ret;
    }

    private Object doExcudeCgformAdd(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = null;
        try {
            HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
            // 判断Header中是否包含 lowAppId
            String lowAppId = request.getHeader(LowAppConst.X_LOW_APP_ID);
            if (StringUtil.isNotBlank(lowAppId)) {
                Object[] args = pjp.getArgs();
                OnlCgformModel model = (OnlCgformModel) args[0];
                OnlCgformHead head = model.getHead();
                // 设置lowAppId
                head.setLowAppId(lowAppId);
                // 直接让方法执行到最后，添加成功后才能获取到添加的ID
                ret = pjp.proceed();
                // 如果不是附表，则添加菜单
                if (!CgformConstant.ONLINE_TABLE_TYPE_SUB.equals(head.getTableType())) {
                    this.saveLowAppMenu(lowAppId, head.getTableTxt(), head.getId(), LowAppMenuType.CGFORM);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ret == null) {
            return pjp.proceed();
        }
        return ret;
    }

    /**
     * 保存lowApp菜单
     *
     * @param lowAppId
     * @param menuName
     * @param menuUrl
     * @param type
     */
    private void saveLowAppMenu(String lowAppId, String menuName, String menuUrl, String type) {
        LowAppMenu entity = new LowAppMenu();
        entity.setAppId(lowAppId);
        entity.setType(type);
        entity.setMenuName(menuName);
        entity.setMenuUrl(menuUrl);
        entity.setDelFlag(CommonConstant.DEL_FLAG_0);
        appMenuService.saveMenu(entity);
    }

    /**
     * 根据menuUrl删除菜单
     *
     * @param menuUrls
     */
    private boolean removeByMenuUrl(String[] menuUrls, String type) {
        // 表单设计器单独处理
        if (LowAppMenuType.DESFORM.equals(type)) {
            return appMenuService.forceRemoveByDesformId(Arrays.asList(menuUrls));
        } else {
            LambdaQueryWrapper<LowAppMenu> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(LowAppMenu::getType, type);
            wrapper.in(LowAppMenu::getMenuUrl, Arrays.asList(menuUrls));
            return appMenuService.forceRemove(wrapper);
        }
    }

    /**
     * 自动判断删除类型（单个删除，批量删除）
     *
     * @param pjp
     * @param type
     */
    private Object autoDeleteMenu(ProceedingJoinPoint pjp, String type) throws Throwable {
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
        // 判断Header中是否包含 lowAppId
        String lowAppId = request.getHeader(LowAppConst.X_LOW_APP_ID);
        if (StringUtil.isNotBlank(lowAppId)) {
            Signature signature = pjp.getSignature();
            Method method = ((MethodSignature) signature).getMethod();
            Object[] args = pjp.getArgs();
            String id = (String) args[0];
            String[] ids;
            if ("deleteBatch".equals(method.getName())) {
                ids = id.split(",");
            } else {
                ids = new String[]{id};
            }
            this.removeByMenuUrl(ids, type);
            return pjp.proceed();
        }
        return null;
    }

    /**
     * Online表单【从数据库导入到Online】方法切面
     */
    private Object doExcudeCgformSaveDbTable2Online(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
        // 1. 先执行完方法
        Object ret = pjp.proceed();
        // 2. 判断Header中是否包含 lowAppId
        String lowAppId = request.getHeader(LowAppConst.X_LOW_APP_ID);
        if (StringUtil.isNotBlank(lowAppId)) {
            // 3. 获取到表名
            Object tableName = pjp.getArgs()[0];
            // 4. 根据表名更新lowAppId
            OnlCgformHead cgFormHead = new OnlCgformHead();
            cgFormHead.setLowAppId(lowAppId);
            LambdaQueryWrapper<OnlCgformHead> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(OnlCgformHead::getTableName, tableName);
            cgformHeadService.update(cgFormHead, queryWrapper);
        }
        return ret;
    }

}
